/*******************************************************************************
 * Copyright (c) 2012, 2013 XML-Print Project.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     XML-Print Project - initial API and implementation
 ******************************************************************************/
/*
 * 
 * 
 * 
 */
package info.xmlprint.run;

import info.xmlprint.core.format.Format;
import info.xmlprint.core.format.FormatGroup;
import info.xmlprint.core.innerlink.BaseItem;
import info.xmlprint.core.innerlink.InnerLink;
import info.xmlprint.core.innerlink.InnerLinkGroup;
import info.xmlprint.core.mapping.MappingGroup;
import info.xmlprint.core.project.PrintProject;
import info.xmlprint.core.xml.SpacingHandle;
import info.xmlprint.font.FontInfo;
import info.xmlprint.font.FontManager;
import info.xmlprint.ui.message.GuiMessages;
import info.xmlprint.util.XmlUtil;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Iterator;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.eclipse.jface.dialogs.MessageDialog;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * XML zum PDF transformieren
 * 
 * 
 */
public class XML2Foplus {

	private Logger logger = Logger.getLogger(this.getClass().getName());

	// Input aus Datei
	// private File xslFile;
	private File xmlFile;

	// Input aus Stream
	private Source src;

	private Transformer transformer;

	private MappingGroup mappingGroup;

	/**
	 * Input definieren
	 * 
	 * @param source
	 *            aus Eclipse-Editor
	 */
	public XML2Foplus(String source) {
		init(); 
		src = sourceValidate(source);
		initFootnoteReference();
	}

	private void init(){
//		System.setProperty("javax.xml.transform.TransformerFactory",
//				"net.sf.saxon.TransformerFactoryImpl");
	}
	/*
	 * Ignore dtd validation
	 */
	private Source sourceValidate(String source) {
		Source out = null;
		try {
			XMLReader reader = XMLReaderFactory.createXMLReader();
			reader.setEntityResolver(new EntityResolver() {
				@Override
				public InputSource resolveEntity(String publicId,
						String systemId) throws SAXException, IOException {
					logger.warn("Ignoring: " + publicId + ", " + systemId);
					return new org.xml.sax.InputSource(
							new java.io.StringReader(""));
				}
			});
			out = new SAXSource(reader, new InputSource(
					new ByteArrayInputStream(source.getBytes("utf-8"))));

		} catch (SAXException e) {
			logger.error("", e);
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			logger.error("", e);
			e.printStackTrace();
		}

		return out;
	}

	/**
	 * Input definieren
	 * 
	 * @param source
	 *            aus Eclipse-Editor
	 */
	public XML2Foplus(String source, MappingGroup mappingGroup) { 
		init(); 
		this.mappingGroup = mappingGroup; 
		src = sourceValidate(source);
		initFootnoteReference();
	}

	/**
	 * Input definieren
	 * 
	 * @param xml
	 *            Input aus XML-Datei
	 * @param xsl
	 *            Stylesheet aus extremem Datei
	 */
	public XML2Foplus(File xsl, File xml) {
		init(); 
		xmlFile = xml;
		src = new StreamSource(xmlFile);
		initFootnoteReference();
		StreamSource ss = getSourceWithUTF8(xsl);
		transformerInit(ss);
	}

	private void initFootnoteReference() {
		TransformerFactory transformerFactory = null;
		try {
			transformerFactory = new net.sf.saxon.TransformerFactoryImpl();
		} catch (Exception e1) {  
			e1.printStackTrace();
		}

		if (mappingGroup == null) {
			PrintProject pProject = RuntimeUtil.getInstance()
					.getCurrentPrintProject();
			if (pProject != null) {
				mappingGroup = pProject.getMappingGroup();
			}
		} 
		Document doc = mappingGroup
				.getXslDocWithFile(Constants.FOOTNOTE_REF_XSLT);

		if (doc == null) {
			return;
		}
 
		try {

			Transformer xformer = transformerFactory
					.newTransformer(new StreamSource(new ByteArrayInputStream(
							doc.asXML().getBytes("utf-8"))));

			// xformer = transformerFactory.newTransformer(new StreamSource(this
			// .getClass()
			// .getResourceAsStream(Constants.FOOTNOTE_REF_XSLT)));

			ByteArrayOutputStream bos = new ByteArrayOutputStream();

			xformer.transform(src, new StreamResult(bos));
			bos.close();

			src = new StreamSource(new ByteArrayInputStream(bos.toByteArray()));

		} catch (TransformerConfigurationException e) {
			logger.error("", e);
			MessageDialog.openError(null, "XML Error:", e.getMessage());
			e.printStackTrace();
			src = null;
		} catch (TransformerException e) {
			logger.error("", e);
			MessageDialog.openError(null, "XML Error:", e.getMessage());
			e.printStackTrace();
			src = null;
		} catch (IOException e) {
			logger.error("", e);
			MessageDialog.openError(null, "XML Error:", e.getMessage());
			e.printStackTrace();
			src = null;
		}

	}

	/**
	 * Lesen File unter utf-8
	 */
	private StreamSource getSourceWithUTF8(String fileName) {
		StreamSource ss = null;
		if (fileName != null && !fileName.equals("")) {
			try {
				ss = new StreamSource(new InputStreamReader(
						new FileInputStream(fileName)), "utf-8");
			} catch (FileNotFoundException e) {
				logger.error("", e);
				e.printStackTrace();
			}
		}
		return ss;
	}

	/**
	 * Lesen File unter utf-8
	 */
	private StreamSource getSourceWithUTF8(File file) {
		StreamSource ss = null;
		if (file != null && file.isAbsolute()) {
			try {
				ss = new StreamSource(new InputStreamReader(
						new FileInputStream(file)), "utf-8");
			} catch (FileNotFoundException e) {
				logger.error("", e);
				e.printStackTrace();
			}
		}
		return ss;
	}

	/**
	 * Input definieren
	 * 
	 * @param xml
	 *            Name des XML-Datei
	 * @param xsl
	 *            Name des Stylesheet
	 */
	public XML2Foplus(String xslName, String xmlName) {
		xmlFile = new File(xmlName);
		StreamSource ss = getSourceWithUTF8(xslName);
		transformerInit(ss);
	}

	public void setXslSour(File f) {
		StreamSource ss = getSourceWithUTF8(f);
		transformerInit(ss);
	}

	public void setXslSour(InputStream in) {
		transformerInit(new StreamSource(in, "utf-8"));
	}

	public void setXslSour(org.dom4j.Document doc) {
		try {
			setXslSour(new ByteArrayInputStream(doc.asXML().getBytes("utf-8")));
		} catch (UnsupportedEncodingException e) {
			logger.error("", e);
			e.printStackTrace();
		}
	}

	private void transformerInit(StreamSource ss) {
		TransformerFactory transformerFactory = new net.sf.saxon.TransformerFactoryImpl();

		if (ss == null) {
			MessageDialog.openError(null, GuiMessages.Stileditor_Error_Error,
					GuiMessages.Stileditor_Error_XSLNull);
			return;
		}

		try {

			transformer = transformerFactory.newTransformer(ss);
		} catch (TransformerConfigurationException e) {
			logger.error("", e);
			MessageDialog.openError(
					null,
					GuiMessages.Stileditor_Error_Error,
					GuiMessages.Stileditor_Error_XSLError + ": "
							+ e.getMessage() + "...");
			e.printStackTrace();
		}
	}

	public String getResultFoFile() {
		if (src == null) {
			return null;
		}

		// Fo ohne leerzeile, false: original
		boolean noBlankRow = true;

		String filePath = Configuration.getInstance().getFoFilePath();
		String tempFilePath = filePath + "o";

		writeFontConfigXml();

		if (!noBlankRow)
			tempFilePath = filePath;

		//transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$

		// transformer.setOutputProperty(
		//"{http://xml.apache.org/xslt}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
		transformer.setOutputProperty(OutputKeys.METHOD, "xml");
		transformer.setOutputProperty(OutputKeys.VERSION, "1.0");
		transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
		transformer.setOutputProperty(OutputKeys.INDENT, "no");
		transformer.setOutputProperty(OutputKeys.MEDIA_TYPE, "text/xml");
		//transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
		//transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
		OutputStreamWriter out;
		try {
			out = new OutputStreamWriter(new FileOutputStream(tempFilePath),
					"UTF-8");
			transformer.transform(src, new StreamResult(out));
			out.close();

		} catch (UnsupportedEncodingException e) {
			logger.error("", e);
			e.printStackTrace();
			return null;
		} catch (FileNotFoundException e) {
			logger.error("", e);
			e.printStackTrace();
			return null;
		} catch (TransformerException e) {
			logger.error("", e);
			e.printStackTrace();

		} catch (IOException e) {
			logger.error("", e);
			e.printStackTrace();
			return null;
		}

		if (noBlankRow)
			getFoNoBlankRow(tempFilePath, filePath);

		new SpacingHandle(filePath);

		return filePath;
	}

	public boolean getFoNoBlankRow(String inputFilePath, String outputFilePath) {

		try {
			FileInputStream fis = new FileInputStream(inputFilePath);
			InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
			BufferedReader br = new BufferedReader(isr);
			FileOutputStream fos = new FileOutputStream(outputFilePath);
			OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
			
			String line = null;
			String newLine = System.getProperty("line.separator");
			if (newLine == null) {
				if (Configuration.isOs("windows"))
					newLine = "\r\n";
				else
					newLine = "\n";
			}
			//osw.write("<?xml version=\"1.0\" ");
	        //osw.write("encoding=\"UTF-8\"?>\r\n"); 
			while ((line = br.readLine()) != null) {
				if (line.trim().isEmpty())
					continue;
				osw.write(line.replaceAll(">\\s+<", "><").trim() + newLine);
				osw.flush();
			}
			osw.close();
			fos.close();
			br.close();
			isr.close();
			fis.close();

		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

	private String writeFontConfigXml() {
		FormatGroup formatGroup = mappingGroup.getFormatGroup();
		if (formatGroup == null)
			return null;

		String filePath = Configuration.getInstance().getFontConfigPathXml();
		// System.out.println(filePath);
		String fontName = null, fontStyle, fontWeight;
		FontInfo fontInfo = null;

		String seperator = "§&@";
		HashSet<String> nameSet = new HashSet<String>();

		for (Format f : formatGroup.getFormats()) {
			fontName = f.getAttribute("font-family");
			fontStyle = f.getAttribute("font-style");
			fontWeight = f.getAttribute("font-weight");

			if (!fontName.trim().equalsIgnoreCase("")) {
				nameSet.add(fontName + seperator + fontStyle + seperator
						+ fontWeight);
			}
		}
		if (nameSet.size() < 1)
			return null;

		Iterator<String> it = nameSet.iterator();
		String nameStr = null;
		String[] nameArr;
		HashSet<FontInfo> fontInfoSet = new HashSet<FontInfo>();
		while (it.hasNext()) {
			nameStr = it.next();
			nameArr = nameStr.split(seperator);
			fontInfo = FontManager.getInstance().getFontInfo(nameArr[0],
					nameArr[1], nameArr[2]);
			if (fontInfo != null) {
				fontInfoSet.add(fontInfo);
			}
		}
		if (fontInfoSet.size() < 1)
			return null;

		Document doc = DocumentHelper.createDocument();
		Element fonts = doc.addElement("fonts");
		fonts.addAttribute("register-system-font-folders", "false");
		Iterator<FontInfo> it2 = fontInfoSet.iterator();
		FontInfo fi;
		while (it2.hasNext()) {
			fi = it2.next();
			fonts.add(fi.getXmlDocument().getRootElement());
		}

		XmlUtil.save2file(doc, filePath);
		return filePath;
	}

	// public String getFoText() {
	// String filePath = getResultFoFile();
	// if (filePath == null)
	// return null;
	//
	// try {
	// InputStream is = new FileInputStream(filePath);
	// BufferedReader reader = new BufferedReader(new InputStreamReader(
	// is, "utf-8"));
	// StringBuilder stringBuilder = new StringBuilder();
	// for (String line = reader.readLine(); line != null; line = reader
	// .readLine()) {
	// stringBuilder.append(line + "\n");
	// }
	// is.close();
	// reader.close();
	//
	// return stringBuilder.toString();
	// } catch (IOException e) {
	// throw new RuntimeException(e);
	// }
	// }

	/**
	 * Ein Fenster zu erzeugen und PDF zu zeigen
	 * 
	 * @param ts
	 *            Werte für Zoom
	 */
	public void preview(double ts, FormatGroup ss, boolean newFo) {

	}

	// SWT composite
	public void getComposite() {

	}

}
